import datetime

from django.db import transaction
from django.db.models import Max
from django.db.models.functions import Coalesce
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from rest_framework.views import APIView

from apps.lookups.calendars.filters import CalendarTypeFilter
from apps.lookups.calendars.handlers import generate_date_range, is_holiday
from apps.lookups.calendars.models import CalendarType, Calendar, CalendarDateType
from apps.lookups.calendars.serializers import CalendarTypeSerializer
from bases.response import SuccessResponse
from bases.viewsets import APIModelViewSet
from utils.utils import update_set, update_instance


class CalendarTypeViewSet(APIModelViewSet):
    queryset = CalendarType.objects.all()
    serializer_class = CalendarTypeSerializer
    filter_class = CalendarTypeFilter

    @transaction.atomic
    def perform_update(self, serializer):
        serializer.save()
        instance = self.get_object()
        # 获取当前日历类型所有日历数据
        calendars = Calendar.objects.filter(calendar_type=instance)
        if calendars.exists():
            # 获取早于今日日历数据的最大workday_seq
            results = calendars.filter(calendar_date__lt=datetime.date.today()).aggregate(
                max_workday_seq=Coalesce(Max('workday_seq'), 0))
            max_workday_seq = results['max_workday_seq']
            # 只修改今天以及未来的日历数据
            target_calendars = calendars.filter(calendar_date__gte=datetime.date.today())

            target_calendar_list = []
            for target_calendar in target_calendars:
                if is_holiday(instance.holiday, str(target_calendar.calendar_date)):
                    target_calendar.workday_seq = 0
                    target_calendar.date_type = CalendarDateType.HOLIDAY
                else:
                    max_workday_seq += 1
                    target_calendar.workday_seq = max_workday_seq
                    target_calendar.date_type = CalendarDateType.WORKDAY
                update_instance(target_calendar, self.request)

                target_calendar_list.append(target_calendar)

            Calendar.objects.bulk_update(target_calendar_list,
                                         ['workday_seq', 'date_type', 'updated', 'updated_date', 'updated_program'])

    @transaction.atomic
    def perform_destroy(self, instance):
        Calendar.objects.filter(calendar_type=instance).delete()
        instance.delete()


class GenerateCalendarApi(APIView):

    @transaction.atomic
    def post(self, request):
        """
        生成年度日历
        请求参数：日历类型：calendar
        @param request:
        @return:
        """
        calendar = request.data.get('calendar_type')
        # Translators: 请求参数“calendar_type”是必须的
        assert calendar, _("%(field)s is require") % {'field': 'calendar_type'}
        year = datetime.datetime.now().year

        try:
            calendar_type = CalendarType.objects.get(calendar_type__exact=calendar)
        except CalendarType.DoesNotExist:
            # Translators: 日历类型不存在
            raise ValidationError(_('calendar type does not exist'))

        calendars = Calendar.objects.filter(calendar_type=calendar_type)

        if calendars.filter(calendar_date__year=year).exists():
            # Translators: year年的日历数据已经存在
            raise ValidationError(_('%(year)d calendar data existed') % {'year': year})

        if calendars.filter(calendar_date__year__gt=year).exists():
            # Translators: 大于year年的日历数据已经存在
            raise ValidationError(_('greater than %(year)d calendar data existed') % {'year': year})

        dates = generate_date_range(int(year))
        calendar_list = []
        results = calendars.aggregate(max_workday_seq=Coalesce(Max('workday_seq'), 0),
                                      max_date_seq=Coalesce(Max('date_seq'), 0))

        max_workday_seq = results['max_workday_seq']
        max_date_seq = results['max_date_seq']

        for date in dates:
            date_str = str(date)
            max_date_seq += 1
            # 判断是否为假期
            if is_holiday(calendar_type.holiday, date_str):
                workday_seq = 0
                date_type = 'H'
            else:
                max_workday_seq += 1
                workday_seq = max_workday_seq
                date_type = 'W'

            calendar_list.append(Calendar(calendar_type=calendar_type, calendar_date=date, date_seq=max_date_seq,
                                          workday_seq=workday_seq, date_type=date_type,
                                          **update_set(request, create=True)))

        Calendar.objects.bulk_create(calendar_list)

        return SuccessResponse()


class ExtendCalendarApi(APIView):

    @transaction.atomic
    def post(self, request):
        """
        扩张年度日历
        请求参数：日历类型：calendar
        @param request:
        @return:
        """
        calendar = request.data.get('calendar_type')
        # Translators: 请求参数“calendar_type”是必须的
        assert calendar, _("%(field)s is require") % {'field': 'calendar_type'}

        try:
            calendar_type = CalendarType.objects.get(calendar_type__exact=calendar)
        except CalendarType.DoesNotExist:
            # Translators: 日历类型不存在
            raise ValidationError(_('calendar type does not exist'))

        calendars = Calendar.objects.filter(calendar_type=calendar_type)
        # Translators: 日历数据不存在，不允许扩张
        # The calendar data does not exist, extension is not allowed.
        assert calendars.exists(), _('calendar extension is not allowed')

        results = calendars.aggregate(max_year=Max('calendar_date__year'))
        max_year = results['max_year']
        target_year = int(max_year) + 1

        dates = generate_date_range(target_year)
        calendar_list = []
        results = calendars.aggregate(max_workday_seq=Coalesce(Max('workday_seq'), 0),
                                      max_date_seq=Coalesce(Max('date_seq'), 0))

        max_workday_seq = results['max_workday_seq']
        max_date_seq = results['max_date_seq']

        for date in dates:
            date_str = str(date)
            max_date_seq += 1
            # 判断是否为假期
            if is_holiday(calendar_type.holiday, date_str):
                workday_seq = 0
                date_type = 'H'
            else:
                max_workday_seq += 1
                workday_seq = max_workday_seq
                date_type = 'W'

            calendar_list.append(Calendar(calendar_type=calendar_type, calendar_date=date, date_seq=max_date_seq,
                                          workday_seq=workday_seq, date_type=date_type,
                                          **update_set(request, create=True)))

        Calendar.objects.bulk_create(calendar_list)

        return SuccessResponse()


class CalendarApi(APIView):

    def get(self, request):
        calendar = request.GET.get('calendar_type')
        year = request.GET.get('year')
        month = request.GET.get('month')
        # Translators: 请求参数“calendar_type”是必须的
        assert calendar, _("%(field)s is require") % {'field': 'calendar_type'}
        # Translators: 请求参数“year”是必须的
        assert year, _("%(field)s is require") % {'field': 'year'}
        # Translators: 请求参数“month”是必须的
        assert month, _("%(field)s is require") % {'field': 'month'}

        calendars = Calendar.objects.filter(calendar_type__calendar_type__exact=calendar, calendar_date__year=year,
                                            calendar_date__month=month)

        if not calendars.exists():
            # Translators: 日历数据不存在
            raise ValidationError(_('calendar data does not exist'))

        calendar_list = []
        for calendar in calendars:
            data = dict()
            data['date'] = calendar.calendar_date.strftime('%Y-%m-%d')
            data['holiday'] = calendar.date_type
            calendar_list.append(data)

        return SuccessResponse(calendar_list)

    @transaction.atomic
    def post(self, request):
        calendar = request.data.get('calendar_type')
        click_date_str = request.data.get('click_date')
        # Translators: 请求参数“calendar_type”是必须的
        assert calendar, _("%(field)s is require") % {'field': 'calendar_type'}
        # Translators: 请求参数“date”是必须的
        assert calendar, _("%(field)s is require") % {'field': 'date'}

        click_date = datetime.datetime.strptime(click_date_str, "%Y-%m-%d").date()
        # Translators: 不允许修改早于今天的日历数据
        assert click_date >= datetime.date.today(), _('not allow adjust calendar data')

        results = Calendar.objects.filter(calendar_type__calendar_type__exact=calendar,
                                          calendar_date__lt=click_date).aggregate(
            max_workday_seq=Coalesce(Max('workday_seq'), 0))
        max_workday_seq = results['max_workday_seq']

        try:
            current_calendar = Calendar.objects.get(calendar_type__calendar_type__exact=calendar,
                                                    calendar_date=click_date)
        except Calendar.DoesNotExist:
            # Translators: 日历数据不存在
            raise ValidationError(_('calendar data does not exist'))

        date_type = current_calendar.date_type
        if date_type == CalendarDateType.HOLIDAY:
            current_calendar.date_type = CalendarDateType.WORKDAY
        else:
            current_calendar.date_type = CalendarDateType.HOLIDAY
            current_calendar.workday_seq = 0
        current_calendar.save()

        calendar_list = []

        calendars = Calendar.objects.filter(calendar_type__calendar_type__exact=calendar,
                                            calendar_date__gte=click_date).order_by('calendar_date')

        for calendar_obj in calendars:
            if calendar_obj.date_type == CalendarDateType.WORKDAY:
                max_workday_seq += 1
                calendar_obj.workday_seq = max_workday_seq
                update_instance(calendar_obj, request)

                calendar_list.append(calendar_obj)

        Calendar.objects.bulk_update(calendar_list, ['workday_seq', 'updated', 'updated_date', 'updated_program'])

        return SuccessResponse()
